//
// Created by luo on 2022-08-26.
//

#include "../opencv/OpenCv.h"

//为了能在被调用函数中使用而设为全局变量
int value;
static void CallBack(int,void*);//滑动条回调函数
Mat img1;

void QuickDemo::Test_trackbar(cv::Mat frame)
{
    //3.7 窗口交互操作
    //3.7.1 图像窗口滑动条
    /*
     * 在显示图像窗口中创建，能够通过滑动改变数值的滑动条，实现动态调参
     * void createTrackbar(trackbarname, winname, &value,count,onChange,userdata)
     * 滑动条名称，创建滑动条窗口名称，整数变量的指针(反应滑块位置)，滑动条最大值
     * 每次滑块更改位置时调用的函数指针，原型是void Foo(int,void*)，轨迹栏位置，用户数据
     * 传递给回调函数的可选参数
     *
     * */
    frame.copyTo(img1);
    //cv::cvtColor(img1,img1,COLOR_BGR2GRAY);
    namedWindow("bar");
    cv::resizeWindow("bar",500,500);
    imshow("bar",img1);

    value = 44;
    //创建滑动条
    createTrackbar("light","bar",&value,255,CallBack,0);
    waitKey(0);
}

static void CallBack(int,void*)
{
//    float a = value / 100.0;
//    img2 = img1*a;//原始图像灰度值*亮度系数
//    imshow("bar0",img2);
//    cv::threshold(img1,img2,value,255,cv::THRESH_BINARY);//x阈值的二值化
//    cv::Mat filterResult_9;
//    cv::medianBlur(img2,img2,9);
//    cv::imshow("img2",img2);

    cv::Mat img2 = img1;
    cv::Mat gray;
    std::vector<cv::Mat> splited;
    cv::split(img2, splited);
    cv::cvtColor(img1,gray,COLOR_BGR2GRAY);
    cv::threshold(gray,gray,48,255,cv::THRESH_BINARY);

    cv::Mat out,out1,out2;
    subtract(splited[2], splited[0], out1);
//    subtract(splited[2], splited[1], out2);
//    bitwise_and(out1,out2,out);
    cv::namedWindow("out_and",WINDOW_NORMAL);
    cv::resizeWindow("out_and",500,500);
    cv::imshow("out_and",out1);

    cv::threshold(out1, out, value, 255, cv::THRESH_BINARY);
    cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(7, 7), cv::Point(-1, -1));
    morphologyEx(out, out, cv::MORPH_DILATE, kernel);
    //morphologyEx(out, out, cv::MORPH_CLOSE, kernel);

    bitwise_and(gray,out,out);
    cv::namedWindow("out",WINDOW_NORMAL);
    cv::resizeWindow("out",500,500);
    cv::imshow("out",out);

    //splited.clear();
}

Mat img,imgPoint; //全局的图像
Point prePoint; //前一时刻鼠标的坐标，用于绘制直线
void mouse(int event,int x,int y,int flags,void*); //鼠标回调函数

void QuickDemo::Test_base(cv::Mat &mat) {
    cout<<"1"<<endl;
    //3.1图像颜色空间
    //1.RGB模型，但在Opencv中是 B G R 排序的
    //2.YUV颜色模型，电视信号系统，亮度
    //3.HSV颜色模型，色度(hue)，饱和度(saturation)，亮度(value)
    //饱和度越高色彩越纯和越艳，越低则色彩越灰暗
    //色度是颜色的基本属性
    //亮度是颜色的明亮程度
    //4.Lab颜色模型，弥补了RGB模型的不足，L表示亮度，ab是两个颜色通道，-128-127
    //5.GRAY灰度模型，0-255从黑到白，相同尺寸相同压缩格式所占容量小、易于采集、便于传输等优点

    /*颜色转换函数
     * void cvtColor(src,dst,code,dstCn)
     * 输入图，输出图，颜色空间转换标志，如COLOR_BGR2GRAY
     * */

    /*多通道分离函数
     * void split(src,mvbegin) split(m,mv)
     * 1.待分离的多通道图像src，分离后的单通道图像mvbegin，为数组形式(Mat a[])
     * 2.待分离的多通道图像m，分离后的单通道图像mv，为向量形式(vector<Mat>)
     * */

    /*
     * 多通道合并函数
     * void merge(mv,count,dst) merge(mv,dst)
     * 1.需要合并的图像数组(每个图像必须拥有相同的尺寸和数据类型)，图像数组长度，合并输出的图像
     * 2.需要合并的图像向量(每个图像必须拥有相同的尺寸和数据类型)。
     * */

    //imshow最多显示4个通道

    //3.2.1图像像素操作处理
    //1.找寻图像像素的最大值和最小值
    /*
     * void minMaxLoc(src,&minVal,&maxVal,&minLoc,&maxLoc,mask)
     * 单通道矩阵src，最小值，最大值，最小值坐标，最大值坐标，掩膜
     * */

    //2.计算图像的平均值和标准差
    /* 平均值越大，图像整体越亮。标准差越大，图像中的明暗变化越明显。
     * cv::Scalar cv::mean(src,mask) 返回 Scalar
     * 待求的平均值图像矩阵，掩模(标记求取哪些区域的平均值)
     * void meanSdtDev(src,mean,stddev,mask)
     * 待求的平均值图像矩阵，图像每个通道的平均值(Mat类型)，..标准差..,掩膜
     * */

    //3.2.2两图像间的像素操作
    //1.两幅图像的比较运算
    /*
     * max(src1,src2,dst) min(src1,src2,dst)
     * 图像尺寸和通道数与数据类型均要一致,dst为保留较大或较小灰度值后的图像矩阵
     * */

    //2.逻辑运算
    /*
     * 与 或 非 异或
     * bitwise_and() / bitwise_or() / bitwise_xor / bitwise_not()
     * src1,src2,dst,mask
     * src,dst,mask
     * */

    //3.2.3图像二值化***
    //“非黑即白的图像”
    /*
     * double threshold(src,dst,thresh,maxval,type)
     * 输入图CV_8U和 CV_32F两种类型src,二值图dst，二值化阈值thresh，
     * 二值化过程的最大值maxval,二值化方法type
     * THRESH_BINARY 灰度值大于阈值的(修改为maxval)，其他值为0
     * THRESH_BINARY_INV 灰度值大于阈值的0，其他值(修改为maxval)
     * THRESH_OTSU 大津法结合图像灰度值分布特性获得二值化的阈值！但只支持输入CV_8UC1的图像
     * */

    /*
     * 局部自适应阈值
     * void adaptiveThreshold(src,dst,maxValue,adaptiveMethod,thresholdType,blockSize,C)
     * 输入图，二值图，二值化最大值，自适应确定阈值的方法ADAPTIVE_THRESH_MEAN_C / 自适应确定阈值的方法ADAPTIVE_THRESH_GAUSSIAN_C
     * 二值化方法(只能是THRESH_BINARY / THRESH_BINARY_INV)，自适应确定阈值的像素邻域大小(3,5,7等奇数)
     * */

//    cv::Mat gray;
//    cv::cvtColor(mat,gray,COLOR_BGR2GRAY);
//    //imshow("gray",gray);
//    cv::Mat img_B,img_B_V,gray_B,gray_B_V,gray_T,gray_T_V,gray_T_RUNC;
//
//    //彩色图像二值化
//    threshold(mat,img_B,125,255,THRESH_BINARY);
//    threshold(mat,img_B_V,125,255,THRESH_BINARY_INV);
////    imshow("img_B",img_B);
////    imshow("img_B_V",img_B_V);
//
//    //灰度图BINARY二值化
//    threshold(gray,gray_B,125,255,THRESH_BINARY);
//    threshold(gray,gray_B_V,125,255,THRESH_BINARY_INV);
////    imshow("gray_B",gray_B);
////    imshow("gray_B_V",gray_B_V);
//
//    //灰度图像TOZERO变换
//    threshold(gray,gray_T,125,255,THRESH_TOZERO);
//    threshold(gray,gray_T_V,125,255,THRESH_TOZERO_INV);
////    imshow("gray_T",gray_T);
////    imshow("gray_T_V",gray_T_V);
//
//    //灰度图像TRUNC变换
//    threshold(gray,gray_T_RUNC,125,255,THRESH_TRUNC);
    //imshow("gray_T_RUNC",gray_T_RUNC);

    //大津法和三角形法
//    Mat img_Thr0,img_ThrT;
//    threshold(gray,img_Thr0,100,255,THRESH_BINARY | cv::THRESH_OTSU);
//    threshold(gray,img_ThrT,125,255,THRESH_TRUNC| cv::THRESH_TRIANGLE);
//    imshow("img_Thr0",img_Thr0);
//    imshow("img_ThrT",img_ThrT);

    //灰度图像自适应二值化
//    Mat adaptive_mean,adaptive_gauss;
//    adaptiveThreshold(gray,adaptive_mean,255,ADAPTIVE_THRESH_MEAN_C,THRESH_BINARY,55,0);
//    adaptiveThreshold(gray,adaptive_gauss,255,ADAPTIVE_THRESH_GAUSSIAN_C,THRESH_BINARY,55,0);
//    imshow("adaptive_mean",adaptive_mean);
//    imshow("adaptive_gauss",adaptive_gauss);
    //cv::waitKey(0);
    //cv::destroyAllWindows();

    //LUT多阈值比较
    //void LUC(src,lut,dst);
    //CV_8U类型输入图src,256个像素灰度值的查找表(单或同src),输出图像矩阵，尺寸=src，类型=lut

    //3.3图像变换
    //3.3.1 图像连接
    /*
     * 图像连接:将两个具有相同高度或者宽度的图像连接在一起
     * void vconcat(&src,nsrc,dst)
     * Mat矩阵类型的数组src，数据中Mat类型数据的数目，连接后的Mat类矩阵
     * 对数组矩阵中的数据纵向连接
     *
     * void vconcat(src1,src2,dst)
     * 对两个Mat类型数据进行竖向连接
     *
     * void hconcat(&src,nsrc,dst)
     * 实现图像或矩阵的左右连接，与vconcat()函数类似
     *
     * void hconcat(src1,src2,dst)
     * 对两个Mat类型数据进行横向连接
     * */

    //3.3.2 图像尺寸变换**
    /*
     * 改变图像的长和宽，实现图像的缩放
     * void resize(src,dst,dsize,fx,fy,interploation)
     * 输入图，输出图，输出尺寸，水平比例，垂直比例(三者共同控制图像的参数)，插值方法
     * 缩小图像 采用INTER_AREA， 放大图像 采用INTER_CUBIC和INTER_LINEAR
     * */

//    cv::Mat gray1 = mat;
//    cv::Mat smallImag,bigImg0,bigImg1,bigImg2;
//    resize(gray1,smallImag,Size(50,50),0,0,INTER_AREA);//先将图片缩小
//    resize(smallImag,bigImg0,Size(100,100),0,0,INTER_NEAREST);
//    resize(smallImag,bigImg1,Size(100,100),0,0,INTER_CUBIC);
//    resize(smallImag,bigImg2,Size(100,100),0,0,INTER_LINEAR);
//    namedWindow("smallImag",WINDOW_NORMAL);
//    namedWindow("bigImg0",WINDOW_NORMAL);
//    namedWindow("bigImg1",WINDOW_NORMAL);
//    namedWindow("bigImg2",WINDOW_NORMAL);
//    imshow("smallImag",smallImag);
//    imshow("bigImg0",bigImg0);
//    imshow("bigImg1",bigImg1);
//    imshow("bigImg2",bigImg2);
//    waitKey(0);

    //3.3.3 图像翻转变换
    /* void flip(src,dst,flipCode)
     * 输入图，输出图，翻转方式标志
     * >0表示绕y轴翻转，<0表示绕两个轴都翻转，=0表示绕x轴翻转
     * */

    //3.3.4 图像仿射变换**
    /*
     * OpenCv4中是通过图像的仿射变换实现图像的旋转
     * 为了旋转图像，首选需要确定旋转角度和旋转中心，之后确定旋转矩阵，最终仿射变换实现图像旋转
     *
     * 计算旋转矩阵: Mat getRotationMatrix2D(center,angle,scale)
     * 图像的中心位置(Point2f center)，图像的旋转角度(正为逆时针)，两个轴的比例因子(用于旋转过程中图像缩放，1为不缩放)
     *
     * 实现仿射变换: void warpAffine(src,dst,M,dsize,flags,borderMode,borderValue)
     * 输入图，输出图，2*3的变换矩阵，输出图尺寸，插值方法，像素边界外推的标志，填充边界使用的数值(0)
     * 第三个参数为getRotationMatrix2D计算出的旋转矩阵
     * 插值方法 WARP_FILL_OUTLIERS WARP_INVERSE_MAP
     * 边界外推方法标志 BORDER_CONSTANT
     * */

    /*
     * 放射变换就是图像的旋转、平移、缩放操作的统称，可以表示是线性变换和平移变幻的叠加
     * 先乘以一个2*2线性变换矩阵，然后加上2*1一个平移向量
     * */

    /*
     * 若已知变换前后两个图像中3个像素点坐标关系，则求得放射变换矩阵M
     * Mat getAffineTransform(src[],dst[])
     * src[]于dst[]为const Point2f类型，源图像、目标图像的三个像素坐标
     * 本函数需要保证像素点的对应关系
     * */

//    Mat rotation0,rotation1,img_wrap0,img_wrap1;
//    double angle = 30; //设置图像旋转的角度
//    Size dst_size(mat.rows,mat.cols); //设置输出图像的尺寸
//    Point2f center(mat.rows/2,mat.cols/2);//设置图像的旋转中心
//    rotation0 = getRotationMatrix2D(center,angle,1);//计算放射变换矩阵
//    warpAffine(mat,img_wrap0,rotation0,dst_size);//进行放射变换
//    imshow("img_wrap0",img_wrap0);

    //根据定义的3个点进行仿射变换
//    Point2f src_points[3];
//    Point2f dst_points[3];
//    src_points[0] = Point2f(0,0); //原始图像中的3个点
//    src_points[1] = Point2f(0,(float)(mat.cols-1));
//    src_points[2] = Point2f((float)(mat.rows-1),(float)(mat.cols-1));
//    //放射变换后图像中的三个点
//    dst_points[0] = Point2f((float)(mat.rows*0.11),(float)(mat.cols*0.2));
//    dst_points[1] = Point2f((float)(mat.rows*0.15),(float)(mat.cols*0.70));
//    dst_points[2] = Point2f((float)(mat.rows*0.81),(float)(mat.cols*0.85));
//    rotation1 = getAffineTransform(src_points,dst_points); //根据对应点求取仿射变换矩阵
//    warpAffine(mat,img_wrap1,rotation1,dst_size);
//    imshow("img_wrap1",img_wrap1);
//    waitKey(0);

    //3.3.5图像透视变换
    /*
     * 透视变换按照物体成像投影规律进行变换，即将物体重新投影到新的成像平面
     * 常用于机器人视觉导航研究，由于相机与地面存在倾斜角，物体成像产生畸变，用于矫正
     * 变换关系用3*3变换矩阵表示，用两幅图4个对应点求取，又称四点变换
     *
     * void getPerspectiveTransform(src[],dst[],solveMethod)
     * src[],dst[]为const Point2f类型，源图像、目标图像的四个像素坐标
     * solveMethod选择放射变换矩阵的方法标志，默认使用DECOMP_LU
     *
     * void warpPerspective(src,dst,M,dsize,flags,boderMode,boderValue)
     * 输入图，输出图，2*3的变换矩阵，输出图尺寸，插值方法，像素边界外推的标志，填充边界使用的数值(0)
     * 第三个参数为getRotationMatrix2D计算出的旋转矩阵
     * 插值方法 WARP_FILL_OUTLIERS WARP_INVERSE_MAP
     * 边界外推方法标志 BORDER_CONSTANT
     *
     * */

    //通过Image Watch/或者角点检测查看二维码4个角点坐标
//    Point2f src_pointss[4];
//    Point2f dst_pointss[4];
//    src_pointss[0] = Point2f(94.0,374.0);
//    src_pointss[1] = Point2f(507.0,380.0);
//    src_pointss[2] = Point2f(1.0,623.0);
//    src_pointss[3] = Point2f(627.0,627.0);
//    //期望透视变换后二维码4个角点的坐标
//    dst_pointss[0] = Point2f(0.0,0.0);
//    dst_pointss[1] = Point2f(627.0,0.0);
//    dst_pointss[2] = Point2f(0.0,627.0);
//    dst_pointss[3] = Point2f(627.0,627.0);
//    Mat rotation,img_warp;
//    rotation = getPerspectiveTransform(src_pointss,dst_pointss);//计算透视变换矩阵
//    warpPerspective(mat,img_warp,rotation,mat.size());//透视变换投影
//    imshow("mat",mat);
//    imshow("mat",mat);
//    waitKey(0);

    //3.3.6 极坐标变换

    //3.4 在图像上绘制几何图形***
    //3.4.1 绘制圆形
    /*
     * void circle(img,center,radius,color,thickness,lineType,shift)
     * 输入图，圆心坐标，半径，颜色，宽度(-1则实心圆),边界类型,中心坐标和半径数值的小数位数
     * 边界类型 FILLED LINE_4 LINE_8 LINE_AA
     * */

    //3.4.2 绘制直线
    /*
     * void line(img,pt1,pt2,color,thickness,lineType,shift)
     * 输入图，起点坐标，终点坐标，颜色，宽度(-1则实心圆),边界类型,中心坐标和半径数值的小数位数
     * */

    //3.4.3 绘制椭圆
    /*
     * void ellipse(img,center,axes,angle,startAngle,endAngle,color,thickness,lineType,shift)
     * 输入图，椭圆中心坐标，椭圆主轴大小一半，椭圆旋转角度，弧起始角度，终止角度
     * */

    /*
     * 输出椭圆边界的像素坐标
     * void ellipse2Poly(center,axes,angle,arcStart,arcEnd,delta,pts)
     * 输入图，椭圆中心坐标，椭圆主轴大小一半，椭圆旋转角度，弧起始角度，终止角度,后续折线顶点之间角度,椭圆边缘像素坐标向量集合(vector<Point>)
     * */

    /*
     * 绘制矩形**
     * void rectangle(img,pt1,pt2,color,thickness,lineType,shift)
     * void rectangle(img,rec,color,thickness,lineType,shift)
     * pt1和pt2是Point类型点集(对角顶点)，rec是矩形左上角顶点和长宽
     *
     * Rect变量，与Point、Vec3b等类型相同，它是表示矩形左上角像素坐标，以及长和宽
     * Rect(x,y,width，height)
     * 其中可以存放的类型数据有int(Rect2i或者Rect),double(Rect2d)和float(Rect2f)
     * */

    /*
     * 绘制多边形
     * void fillPoly(img,pts,npts,ncontours,offset)
     * 输入图，多边形顶点(坐标)数组(可以存放多个)，每个多边形顶点数组中顶点的个数，绘制多边形的个数，所有顶点的可选偏移
     * 多边形顶点需逆/顺时针给出，
     * */

    /*
     * 生成文字**
     * void putText(img,text,org,fontFace,fontScale,color,thinckness,lineType,bootomLeftOrigin)
     * 输入图,输出到图像的文字，图像文字左下角像素坐标，字体类型标志，字体大小，图像速狙的原点位置(bool)
     * FONT_HERSHEY_SIMPLEX 正常大小的无衬线字体 FONT_HERSHEY_PLAIN 小尺寸的无衬线字体
     * */

    //绘制几何图形
//    Mat img_ = Mat::zeros(Size(512,512),CV_8UC3);
//    circle(img_,Point(50,50),25,Scalar(255,255,255),-1);//实心圆
//    circle(img_,Point(100,50),20,Scalar(255,255,255),4);
//    line(img_,Point(100,100),Point(200,100),Scalar(255,255,255),2,LINE_4,0);
//    ellipse(img_,Point(300,255),Size(100,70),0,0,100,Scalar(255,255,255),-1);
//    ellipse(img_,RotatedRect(Point2f(150,100),Size2f(30,20),0),Scalar(255,255,255),2);
//    vector<Point>points;
//    //用一些点来近似一个椭圆
//    ellipse2Poly(Point(200,400),Size(100,70),0,0,360,2,points);
//    for(auto i = 0; i < points.size()-1;i++)//直线画出椭圆
//    {
//        if(i == points.size()-1)
//        {
//           //椭圆最后一个点与第一个点连线
//           line(img_,points[0],points[i],Scalar(255,255,255),2);
//           break;
//        }
//        //当前点与后一个点连线
//        line(img_,points[i],points[i+1],Scalar(255,255,255),2);
//    }
//    //绘制矩形
//    rectangle(img_,Point(50,400),Point(100,450),Scalar(125,125,125),-1);
//    rectangle(img_,Rect(400,450,60,50),Scalar(0,125,125),2);
    //绘制多边形
//    Point pp[2][6];
//    pp[0][0] = Point(72,200);
//    pp[0][1] = Point(142,204);
//    pp[0][2] = Point(226,263);
//    pp[0][3] = Point(172,310);
//    pp[0][4] = Point(117,319);
//    pp[0][5] = Point(15,260);
//    pp[1][0] = Point(359,339);
//    pp[1][1] = Point(447,351);
//    pp[1][2] = Point(504,349);
//    pp[1][3] = Point(484,433);
//    pp[1][4] = Point(418,449);
//    pp[1][5] = Point(354,402);
//    Point pp1[5];
//    pp1[0] = Point(350,83);
//    pp1[1] = Point(463,90);
//    pp1[2] = Point(500,171);
//    pp1[3] = Point(421,194);
//    pp1[4] = Point(338,141);
//    const Point* pts[3] = {pp[0],pp[1],pp1}; //ppts变量的生成
//    int npts[] = {6,6,5}; //顶点个数数组的生成
    //fillPoly(img_,pts,npts,3,Scalar(125,125,125),8);//绘制三个多边形
    //生成文字
    //putText(img_,"Project 4 ",Point(100,400),2,1,Scalar(255,255,0));
//    imshow("img_",img_);
//    waitKey(0);

    //3.5 感兴趣区域
    /*
     * 感兴趣区域:图像中的部分区域，截取出来再进行处理，减少内存负担
     * 从原图截取，即标记截取部分位置，可用Rect/Range数据结构标记
     * Rect_(_Tp _x,_Tp _y, _Tp _width, _Tp _height)
     * cv::range(int start, int end)
     * _Tp 数据类型 C++模板，可用int，double，float替换
     * _x 矩形左上角第一个像素x坐标，第一个像素的列数
     * _y 矩形左上角第一个像素y坐标，第一个像素的行数
     * _width 矩形的宽，单位为像素，即矩形区域跨越的列数
     * _height 矩形的高，单位为像素，即矩形区域跨越的行数
     * start 区间的起始
     * end 区间的结束
     * 如在img中截取图像，img(Rect(p.x,p.y,width,height))实现
     * img(Range(rows_start,rows_end),Range(cols_start,cols_end))
     * */

    /*
     * Opencv4中图像的浅拷贝和深拷贝
     * 浅拷贝:相当于引用,"="的赋值方式，当前变量改变，会导致原始变量的改变
     * 深拷贝:在创建变量的同时在内容中分配新的地址用于存储数据，新/旧访问数据地址不同，不会改变
     * */

    /*
     * 深拷贝
     * void cv::Mat::copyTo(OutputArry m) const
     * void cv::Mat::copyTo(OutputArry m, InputArray mask) const
     * void cv::copyTo(InputArry src, OutputArray dst, InputArray mask) const
     * 在两个不同的类中有两个同名函数
     * mask 为掩模矩阵CV_8U类型
     * */

//    Mat ROI = imread("C:\\Pictures\\data\\apple.jpg");
//    if (ROI.empty()) {
//        cout << "Error" << endl;
//        return ;
//    }
//    Mat ROI1,ROI2,ROI2_copy,mask,img2,img_copy,img_copy2;
//    resize(ROI,mask,Size(200,200));
//    img2 = mat; //浅拷贝
    //深拷贝的两种方式
//    mat.copyTo(img_copy2);
//    copyTo(mat,img_copy,mat);
    //两种在图中截取ROI的方式
//    Rect rect(206,206,200,200);//定义ROI
//    ROI1 = mat(rect);//截图
//    ROI2 = mat(Range(300,500),Range(300,500));//第二种截图
//    mat(Range(300,500),Range(300,500)).copyTo(ROI2_copy);//深拷贝
//    mask.copyTo(ROI1);//在图像中加入部分图像(ROI苹果图放到mat里面)
//    imshow("加入ROI后的图像",mat);
//    imshow("ROI对ROI2的影响",ROI2);
//    imshow("深拷贝的ROI2_copy",ROI2_copy);
    //circle(mat,Point(300,300),20,Scalar(0,0,255),-1);//绘制圆
//    imshow("浅拷贝的img2",img2);
//    imshow("深拷贝的img_copy",img_copy);
//    imshow("深拷贝的img_copy2",img_copy2);
//    imshow("画圆对ROI1的影响",ROI1);
//    waitKey(0);


    //3.6 图像"金字塔"

    /*
     * 通过多个分辨率表示图像的一种有效且简单的结构
     * 金字塔形状排列，分辨率逐渐降低的图像集合，底部是待处理图像高分辨率，顶部是低分辨率表示
     * 高斯金字塔，解决尺度不确定性的一种常用方法，下采样不断将图像尺寸缩小，进而在图像"金字塔"包含多个尺寸图像
     * 一般，底层为图像原图，每上一层就下采样缩小一次尺寸，通常是原来的一半
     *
     * void pyrDown(src,dst,dstsize,borderType)
     * 输入待下采样图，输出下采样后图，输出图尺寸，边界外推方法标志
     * 可用于图像模糊并对其下采样,通常为原来一半，但也可以通过dstsize参数调节
     *
     * 拉普拉斯金字塔，与高斯金字塔反过来，通过上层小尺寸构建下层大尺寸，上采样
     * 可预测残差，与高斯一起使用，对于第i层，利用高斯下采样得到尺寸缩小一半的图，即i+1层或不在高斯中
     * 之后对该图上采样，将图像尺寸恢复到第i层图像，最后求取i层(高斯)和上采样后的i层(拉普拉斯)作差，就是拉普拉斯第i层图像
     * void pyrUp(src,dst,dstsize,boderType)
     * */
//    cout<<"11"<<endl;
//    vector<Mat> Gauss,Lap; // 高斯与拉普拉斯 金字塔
//    int level = 3;//高斯下采样次数
//    Gauss.push_back(mat);//原图作为高斯的第0层
    //构建高斯金字塔
//    for(int i = 0; i<level; i++)
//    {
//        Mat gauss;
//        pyrDown(Gauss[i],gauss);
//        Gauss.push_back(gauss);
//    }
//    //构建拉普拉斯金字塔
//    for(int i = Gauss.size() -1; i>0; i--)//下标从0开始，但size从1开始数
//    {
//        Mat lap,upGauss;
//        if(i == Gauss.size() -1 ) //若是高斯最上面一层图像
//        {
//            Mat down;
//            pyrDown(Gauss[i],down); //最上层图先高斯然后再拉普拉斯
//            pyrUp(down,upGauss); //上采样
//            lap = Gauss[i] - upGauss;
//            Lap.push_back(lap);
//        }
//        pyrUp(Gauss[i],upGauss); //上采样
//        lap = Gauss[i-1] - upGauss;
//        Lap.push_back(lap);
//    }
    //查看两个图像"金字塔"图
//    for(int i = 0; i<Gauss.size(); i++)
//    {
//        string name = to_string(i);
//        imshow("G"+name,Gauss[i]);
//        imshow("L"+name,Lap[i]);
//    }
//    waitKey(0);



    //3.7.2 鼠标相应
    /*
     * 在图像中标记出重要的区域，为指定图像窗口创建鼠标响应
     * void setMouseCallback(winname,onMouse,userdata);
     * 添加鼠标响应的窗口名，鼠标响应的回调函数，传递给回调函数的可选参数
     *
     * void MouseCallback(event,x,y,flags,userdata);
     * 鼠标响应事件标志，鼠标指针的x坐标，y坐标，响应标志，传递给回调函数的可选参数
     * 鼠标响应事件标志 EVENT_MOUSEMOVE 鼠标指针在窗口上移动 EVENT_LBUTTONDOWN 按下鼠标左键
     * 回调函数鼠标响应标志 EVENT_FLAG_LBUTTON 按住左键拖曳
     * */
    /*
    cout<<"111"<<endl;
    mat.copyTo(img);
    img.copyTo(imgPoint);
    imshow("window1",img);
    imshow("window2",imgPoint);
    setMouseCallback("window1",mouse,0); //鼠标响应
    waitKey(0);
     */
}

void mouse(int event,int x,int y,int flags,void*)
{
    if(event == EVENT_RBUTTONDOWN) //单击右键
    {
        cout<<"only left key can draw trace! "<<endl;
    }
    if(event == EVENT_LBUTTONDOWN) //单击左键，输出坐标
    {
        prePoint = Point(x,y);
        cout<<"the trace beginning xy"<<prePoint<<endl;
    }
    if(event == EVENT_MOUSEMOVE && (flags & EVENT_FLAG_LBUTTON)) //按住鼠标移动
    {
        //通过改变图像像素显示鼠标移动轨迹
        imgPoint.at<Vec3b>(y,x) = Vec3b(0,0,255);
        imgPoint.at<Vec3b>(y,x-1) = Vec3b(0,0,255);
        imgPoint.at<Vec3b>(y,x+1) = Vec3b(0,0,255);
        imgPoint.at<Vec3b>(y+1,x) = Vec3b(0,0,255);
        imgPoint.at<Vec3b>(y+1,x) = Vec3b(0,0,255);
        imshow("window2",imgPoint);

        //通过绘制直线显示鼠标移动轨迹
        Point pt(x,y);
        line(img,prePoint,pt,Scalar(0,0,255),2,5,0);
        prePoint = pt; //记录当前点
        imshow("window1",img);
    }
}




